package com.aigeth.batisbase;

import jdk.nashorn.internal.objects.annotations.Where;
import org.apache.ibatis.annotations.Options;
import org.apache.ibatis.jdbc.SQL;
import org.apache.ibatis.type.JdbcType;

import java.io.File;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.security.cert.TrustAnchor;
import java.util.ArrayList;
import java.util.List;

public class BaseSqlProvider<T> {
    public boolean ShowSQLLog = false;
    boolean hasdel = false, hassort = false;

    /***
     * 增加一条记录
     * @param bean 增加记录的实体类
     * @return
     */
    @Options
    public String add(T bean) {
        SQL sql = new SQL();
        Class clazz = bean.getClass();
        String tableName = clazz.getSimpleName();
        String realTableName = Tool.humpToLine(tableName).replaceAll("_entity", "").substring(1);
        sql.INSERT_INTO(realTableName);

        List<Field> fields = getAllFields(clazz);
        for (Field field : fields) {
            field.setAccessible(true);
            String column = field.getName();
            PrimaryKey key = field.getAnnotation(PrimaryKey.class);
            if (key == null) {
                sql.VALUES(Tool.humpToLine(column), String.format("#{%s,jdbcType=%s}", column, getSQLTypeByJType(field)));
            }
        }
        if (ShowSQLLog)
            System.out.println(sql.toString());
        return sql.toString();
    }

    /***
     * 删除一条记录
     * @param bean
     * @return
     */
    public String delete(T bean) {
        return deleteWhere(bean, null);
    }

    /***
     * 通过列条件删除记录
     * @param bean
     * @param where
     * @return
     */
    public String deleteWhere(T bean, String... where) {
        SQL sql = new SQL();
        Class clazz = bean.getClass();
        String tableName = clazz.getSimpleName();
        String realTableName = Tool.humpToLine(tableName).replaceAll("_entity", "").substring(1);
        if (hasdel) {
            sql.UPDATE(realTableName);
            sql.SET("isdel=true");
        } else {
            sql.DELETE_FROM(realTableName);
        }
        List<Field> lstFieldAll = getAllFields(clazz);
        if (where == null || where.length <= 0) {
            return getSqlByPrimarkKey(sql, lstFieldAll);
        } else {
            return getSqlByWhere(sql, where);
        }
    }

    /***
     * 通过自定义条件删除记录
     * @param bean
     * @param where
     * @return
     */
    public String deleteWhere2(T bean, String where) {
        SQL sql = new SQL();
        Class clazz = bean.getClass();
        String tableName = clazz.getSimpleName();
        String realTableName = Tool.humpToLine(tableName).replaceAll("_entity", "").substring(1);
        if (hasdel) {
            sql.UPDATE(realTableName);
            sql.SET("isdel=true");
        } else {
            sql.DELETE_FROM(realTableName);
        }
        List<Field> lstFieldAll = getAllFields(clazz);
        if (where == null || where.length() <= 0) {
            return getSqlByPrimarkKey(sql, lstFieldAll);
        } else {
            sql.WHERE(where);
            return sql.toString();
        }
    }

    /**
     * 获取一条记录
     *
     * @param bean
     * @param where
     * @return
     */
    public String get(T bean, String... where) {
        SQL sql = new SQL();
        Class clazz = bean.getClass();
        String tableName = clazz.getSimpleName();
        String realTableName = Tool.humpToLine(tableName).replaceAll("_entity", "").substring(1);
        sql.SELECT("*").FROM(realTableName);

        List<Field> lstFieldAll = getAllFields(clazz);

        if (where != null && where.length > 0)
            return getSqlByWhere(sql, where);
        else
            return getSqlByPrimarkKey(sql, lstFieldAll);
    }

    /*** 通过条件获取列
     * @param cols
     * @param where
     * @return
     */
    public String findColsByWhere(String cols, String where) {
        Class clazz = (Class<T>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
        SQL sql = new SQL();
//        Class clazz = bean.getClass();
        String tableName = clazz.getSimpleName();
        String realTableName = Tool.humpToLine(tableName).replaceAll("_entity", "").substring(1);
        sql.SELECT(cols).FROM(realTableName);

        if (where != null && where.length() > 0) {
            sql.WHERE(where);
        }
        return sql.toString();
    }

    SQL findByWhere(T bean, String cols, String where) {
        SQL sql = new SQL();
        Class clazz = (Class<T>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
//        Class clazz = bean.getClass();
        String tableName = clazz.getSimpleName();
        String realTableName = Tool.humpToLine(tableName).replaceAll("_entity", "").substring(1);
        sql.SELECT(cols).FROM(realTableName);

        if (where != null && where.length() > 0)
            sql.WHERE(where);
        else {
            if (bean != null) {
                List<Field> lstTField = getAllFields(bean.getClass());
                for (Field field : lstTField) {
                    field.setAccessible(true);
                    try {
                        Object obj = field.get(bean);
                        if (obj != null) {
                            sql.WHERE(Tool.humpToLine(field.getName()) + "=" + field.getName());
                        }
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                    }
                }
            } else {
                sql.WHERE("1=2");
            }
        }
        return sql;
    }

    public String findList(T bean) {
        return findByWhere(bean, "*", "").toString();
    }

    public String findList2(String where) {
        return findByWhere(null, "*", where).toString();
    }

    public String findListPage(T bean, String orderby) {
        SQL sql = findByWhere(bean, "*", "");

        Integer pageIndex = PageConst.pageIndex.get();
        Integer pageSize = PageConst.pageSize.get();
        pageIndex = pageIndex - 1;
        if (pageIndex < 0) {
            pageIndex = 0;
        }

        return String.format("%s order by %s limit %d,%d", sql.toString(), orderby, pageIndex * pageSize, (pageIndex + 1) * pageSize);
    }

    public String findListPage2(T bean, String orderby) {
        SQL sql = findByWhere(bean, "*", "");

        Integer pageIndex = PageConst.pageIndex.get();
        Integer pageSize = PageConst.pageSize.get();
        pageIndex = pageIndex - 1;
        if (pageIndex < 0) {
            pageIndex = 0;
        }

        return String.format("%s order by %s limit %d,%d", sql.toString(), orderby, pageIndex * pageSize, (pageIndex + 1) * pageSize);
    }

    public String findCount(T bean) {
        return findByWhere(bean, "count(1) count", "").toString();
    }

    public String updateWhere(T bean, String... where) {
        SQL sql = new SQL();
        Class clazz = bean.getClass();
        String tableName = clazz.getSimpleName();

        String realTableName = Tool.humpToLine(tableName).replaceAll("_entity", "").substring(1);
        sql.UPDATE(realTableName);

        List<Field> fields = getAllFields(clazz);
        for (Field field : fields) {
            field.setAccessible(true);
            String column = field.getName();
            if (column.equals("id")) {
                continue;
            }
            try {
                Object obj = field.get(bean);
                if (obj != null) {
                    sql.SET(Tool.humpToLine(column) + "=" + String.format("#{%s,jdbcType=%s}", column, getSQLTypeByJType(field)));
                }
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }

        if (where == null || where.length <= 0)
            return getSqlByPrimarkKey(sql, fields);
        else
            return getSqlByWhere(sql, where);
    }

    public String update(T bean) {
        return updateWhere(bean, null);
    }

    String getSqlByPrimarkKey(SQL sql, List<Field> lstFieldAll) {
        List<Field> primaryKeyField = getPrimarkKeyFields(lstFieldAll);
        if (!primaryKeyField.isEmpty()) {
            for (Field pkField : primaryKeyField) {
                pkField.setAccessible(true);
                sql.WHERE(pkField.getName() + "=" + String.format("#{" + pkField.getName() + "}"));
            }
        } else {
            sql.WHERE(" 1=2");
            throw new RuntimeException("对象中未包含PrimaryKey属性");
        }
        if (ShowSQLLog)
            System.out.println(sql.toString());
        return sql.toString();
    }

    String getSqlByWhere(SQL sql, String... where) {
        for (String sw : where) {
            sql.WHERE(Tool.humpToLine(sw) + "=#{" + sw + "}");
        }
        return sql.toString();
    }

    private List<Field> getPrimarkKeyFields(List<Field> fields_all) {
        List<Field> primaryKeyField = new ArrayList<Field>();
        for (Field field : fields_all) {
            field.setAccessible(true);
            PrimaryKey key = field.getAnnotation(PrimaryKey.class);
            if (key != null) {
                primaryKeyField.add(field);
            }
        }
        return primaryKeyField;
    }

    private List<Field> getAllFields(Class clazz) {
        List<Field> fieldList = new ArrayList<Field>();
        Field[] fields = clazz.getDeclaredFields();
        for (Field field : fields) {
            field.setAccessible(true);
            Exclude key = field.getAnnotation(Exclude.class);
            if (key == null) {
                fieldList.add(field);
            }
            if (field.getName().equals("isdel")) {
                hasdel = true;
            } else if (field.getName().equals("sort")) {
                hassort = true;
            }
        }
        return fieldList;
    }

    String getSQLTypeByJType(Field f) {
        String name = f.getType().getName();
        DbType dbType = f.getAnnotation(DbType.class);
        if (dbType != null && dbType.value() != null) {
            return dbType.value();
        }
        if (name.equals("java.lang.String")) {
            return "VARCHAR";
        } else if (name.equals("java.lang.Long")) {
            return "BIGINT";
        } else if (name.equals("java.lang.Integer")) {
            return "INTEGER";
        } else if (name.equals("java.lang.Boolean")) {
            return "BIT";
        } else if (name.equals("java.lang.Float")) {
            return "FLOAT";
        } else if (name.equals("java.lang.Double")) {
            return "DOUBLE";
        } else if (name.equals("java.lang.BigDecimal")) {
            return "DECIMAL";
        } else if (name.equals("java.lang.Date")) {
            return "DATE";
        } else if (name.equals("java.lang.Time")) {
            return "TIME";
        } else if (name.equals("java.lang.Timestamp")) {
            return "DATETIME";
        } else {
            return "VARCHAR";
        }
    }

}
